/*
 * @Descripttion: 
 * @version: 
 * @Author: ankeji
 * @Date: 2020-06-09 16:03:53
 * @LastEditors: ankeji
 * @LastEditTime: 2021-03-03 12:01:16
 */
import axios from 'axios'
import util from "../util/util"
import qs from "qs";
import axiosConfig from "./axiosConfig";
import localforage from 'localforage';
const pending = new Map()

/**
 * 添加请求
 * @param {Object} config
 **/
const addPending = (config) => {
    const url = [
        config.method,
        config.url,
        qs.stringify(config.data)
    ].join('&')
    if (pending.has(url)) { // 如果 pending 中存在当前请求则取消后面的请求
        config.cancelToken = new axios.CancelToken(cancel => cancel(`重复的请求被主动拦截: ${url}`))
    } else { // 如果 pending 中不存在当前请求，则添加进去
        config.cancelToken = config.cancelToken || new axios.CancelToken(cancel => {
            pending.set(url, cancel)
        })
    }
}

/**
 * 移除请求
 * @param {Object} config
 */
const removePending = (config) => {
    const url = [
        config.method,
        config.url.replace(config.baseURL, ''), // 响应url会添加域名，需要去掉与请求URL保持一致
        qs.stringify(JSON.parse(config.data)) // 需要与request的参数结构保持一致，request中是对象，response中是字符串
    ].join('&')
    if (pending.has(url)) { // 如果在 pending 中存在当前请求标识，取消当前请求，并且移除
        pending.delete(url)
    }
}

// axios.js
/**
 * 清空 pending 中的请求（在路由跳转时调用）
 */
export const clearPending = () => {
  for (const [url, cancel] of pending) {
    cancel(url)
  }
  pending.clear()
}


/**
 * @description 创建请求实例
 */
function createService(intercept = true) {
    // 创建一个 axios 实例
    const service = axios.create({
        baseURL: axiosConfig.baseURL, // api的base_url
        timeout: axiosConfig.timeout, // 请求超时时间
    })
    // 请求拦截
    service.interceptors.request.use(
        intercept ? config => {
            addPending(config) // 将当前请求添加到 pending 中
            if (util.cookies.get("token")) {
                config.headers.Authorization = "Bearer" + " " + util.cookies.get("token");
            }
            config.headers["Content-Type"] = "application/json";
            return config;
        } : config => config,
        error => {
            // 发送失败
            console.log(error)
            return Promise.reject(error)
        }
    )
    // 响应拦截
    service.interceptors.response.use(
        response => {
            removePending(response.config) // 在请求结束后，移除本次请求
            return response;
        },
        error => {
            if (error && error.response) {
                removePending(error.config) // 在请求结束后，移除本次请求
                switch (error.response.status) {
                    case 400: error.message = '请求错误'; break
                    case 401: error.message = '未授权，请登录'; break
                    case 403: error.message = '拒绝访问'; break
                    case 404: error.message = `请求地址出错`; break
                    case 408: error.message = '请求超时'; break
                    case 500: error.message = '服务器内部错误'; break
                    case 501: error.message = '服务未实现'; break
                    case 502: error.message = '网关错误'; break
                    case 503: error.message = '服务不可用'; break
                    case 504: error.message = '网关超时'; break
                    case 505: error.message = 'HTTP版本不受支持'; break
                    default: break
                }
                return Promise.reject(error.response.data)
            } else {
                // 处理断网的情况
                // eg:请求超时或断网时，更新state的network状态
                // network状态在app.vue中控制着一个全局的断网提示组件的显示隐藏
                // 关于断网组件中的刷新重新获取数据，会在断网组件中说明
                if (!window.navigator.onLine) {
                    error.message = '您当前没有网络！'
                } else {
                    return Promise.reject(error);
                }
            }
        }
    )
    return service
}


// 用于真实网络请求的实例和请求方法
const service = createService();//默认拦截
const request = createService(false);//传false就是不拦截



// 接口的请求类型
// get请求不处理数据类型的
var CancelToken = axios.CancelToken;
let cancelRequest;
const method = {};


/**
 * 不拦截的接口封装开始
 */
method.noGet = (url, config = {}) => {
    return request.get(url, config);
}
//post请求类型，不处理数据类型的
method.nopostJson = (url, body = {}, config = {}) => {
    return request.post(url, body, config);
}
method.noPostFormData = (url, body = {}, config = {}) => {
    let f = new FormData();
    Object.entries(body).forEach(data => {
        f.append(...data);
    });
    return request.post(url, f, config);
}
//不拦截的接口封装结束

//需要被缓存的接口和数据，可以过滤没必要的请求，默认实践3分钟
method.getIfAbsent = (url, config = {}) => {
    return localforage.getItem(url).then(async res => {
        if (!res || res.expire < new Date().getTime()) {
            const resp = await service.get(url, config);
            if (resp.status == 200) {
                localforage.setItem(url, { data: { status: resp.status, data: resp.data }, expire: new Date().getTime() + 30 * 60 * 1000 })
            }
            return resp
        }
        return res.data
    }).catch(err => {
        console.log(err, "vls");
    })
}
// 可以取消前一次请求的接口封装，只执行最后一次请求的接口
method.getOrCancel = (url) => {
    if (cancelRequest) {
        cancelRequest()
    }
    return service.get(url, {
        cancelToken: new CancelToken(c => {
            cancelRequest = c;
        })
    }).then(res => {
        cancelRequest = null
        return res
    });
}
// 可以取消前一次请求的接口封装，只执行最后一次请求的接口
method.postJsonOrCancel = (url, params) => {
    if (cancelRequest) {
        cancelRequest()
    }
    return service.post(url, params, {
        cancelToken: new CancelToken(c => {
            cancelRequest = c;
        })
    }).then(res => {
        cancelRequest = null
        return res
    });
}

method.get = (url, config = {}) => {
    return service.get(url, config);
}

method.del = (url, body = {}, config = {}) => {
    config.params = body;
    return service.delete(url, body, config);
}

//put请求类型，不处理数据类型的
method.putJson = (url, body = {}, config = {}) => {
    return service.put(url, body, config);
}

//post请求类型，不处理数据类型的
method.postJson = (url, body = {}, config = {}) => {
    return service.post(url, body, config);
}


//post请求接口，用了qs做了处理，传过去的参数不带引号
method.postForm = (url, body = {}, config = {}) => {
    return service.post(url, qs.stringify(body), config);
}

method.postFormData = (url, body = {}, config = {}) => {
    let f = new FormData();
    Object.entries(body).forEach(data => {
        f.append(...data);
    });
    return service.post(url, f, config);
}

export default method;